package cli

import (
	"strings"

	"github.com/spf13/cobra"
)

type Runnable func(c *Command, args []string)

type Option interface {
	Apply(c *Command)
}

type OptionApply func(c *Command)

func (opt OptionApply) Apply(c *Command) {
	opt(c)
}

type options []Option

func Group(opts ...Option) Option {
	return options(opts)
}

func (opts options) Apply(c *Command) {
	for _, opt := range opts {
		opt.Apply(c)
	}
}

func Apply(c *Command, cmdOpts ...Option) {
	for _, cmdOpt := range cmdOpts {
		cmdOpt.Apply(c)
	}
}

func Use(use string, alias ...string) Option {
	return OptionApply(func(c *Command) {
		c.Use = use
		if len(alias) > 0 {
			c.Aliases = alias
		}
	})
}

func Alias(alias ...string) Option {
	return OptionApply(func(c *Command) {
		c.Aliases = alias
	})
}

func Version(version string) Option {
	return OptionApply(func(c *Command) { c.Version = version })
}

func Comment(short string, long ...string) Option {
	return OptionApply(func(c *Command) {
		if short != "" {
			c.Short = short
		}
		if len(long) > 0 {
			c.Long = strings.Join(long, "\n")
		}
	})
}

func Flags(appFlag func(fs *FlagSet)) Option {
	return OptionApply(func(c *Command) { appFlag(c.Flags()) })
}

func StructFlags(appFlag func(fs *AnyFlag)) Option {
	return OptionApply(func(c *Command) { appFlag(StructFlag(c.Flags())) })
}

func PersistentFlags(appFlag func(fs *FlagSet)) Option {
	return OptionApply(func(c *Command) { appFlag(c.PersistentFlags()) })
}

func SubCommand(commands ...*Command) Option {
	return OptionApply(func(c *Command) { c.AddCommand(commands...) })
}

func PersistentPreRun(run Runnable) Option {
	return OptionApply(func(c *Command) { c.PersistentPreRun = func(cmd *cobra.Command, args []string) { run(&Command{Command: *cmd}, args) } })
}

func PreRun(run Runnable) Option {
	return OptionApply(func(c *Command) { c.PreRun = func(cmd *cobra.Command, args []string) { run(&Command{Command: *cmd}, args) } })
}

func Run(run Runnable) Option {
	return OptionApply(func(c *Command) { c.Run = func(cmd *cobra.Command, args []string) { run(&Command{Command: *cmd}, args) } })
}

func PostRun(run Runnable) Option {
	return OptionApply(func(c *Command) { c.PostRun = func(cmd *cobra.Command, args []string) { run(&Command{Command: *cmd}, args) } })
}

func PersistentPostRun(run Runnable) Option {
	return OptionApply(func(c *Command) { c.PersistentPostRun = func(cmd *cobra.Command, args []string) { run(&Command{Command: *cmd}, args) } })
}

func ValidArgs(validArgs ...string) Option {
	return OptionApply(func(c *Command) { c.ValidArgs = validArgs })
}

func ValidArgsFunction(validFunc func(c *Command, args []string, toComplete string) ([]string, ShellCompDirective)) Option {
	return OptionApply(func(c *Command) {
		c.ValidArgsFunction = func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
			return validFunc(&Command{Command: *cmd}, args, toComplete)
		}
	})
}

func Args(args PositionalArgs) Option {
	return OptionApply(func(c *Command) { c.Args = args })
}

func DisableFlagsInUseLine(disabled ...bool) Option {
	return OptionApply(func(c *Command) { c.DisableFlagsInUseLine = len(disabled) > 0 && disabled[0] })
}

func DisableSuggestions(disabled ...bool) Option {
	return OptionApply(func(c *Command) { c.DisableSuggestions = len(disabled) > 0 && disabled[0] })
}
func DisableAutoGenTag(disabled ...bool) Option {
	return OptionApply(func(c *Command) { c.DisableAutoGenTag = len(disabled) > 0 && disabled[0] })
}

func Hidden(hidden ...bool) Option {
	return OptionApply(func(c *Command) { c.Hidden = len(hidden) > 0 && hidden[0] })
}

func DisableFlagParsing(disabled ...bool) Option {
	return OptionApply(func(c *Command) { c.DisableFlagParsing = len(disabled) > 0 && disabled[0] })
}

func ArgAliases(alias ...string) Option {
	return OptionApply(func(c *Command) { c.ArgAliases = alias })
}

func Long(long ...string) Option {
	return OptionApply(func(c *Command) { c.Long = strings.Join(long, "\n") })
}
